#include "base/log.h"

#include <bits/stl_algo.h>
#include <memory.h>
#include <sys/time.h>
#include <time.h>

#include "base/logstream.h"
namespace net_tools::base
{
  namespace detail
  {
    const char digitsHex[] = "0123456789ABCDEF";
    const char digits[] = "9876543210123456789";
    const char *zero = digits + 9;
    template <typename T>
    short int Convert(char buf[], T value)
    {
      T i = value;
      char *p = buf;
      do
      {
        int lsd = static_cast<int>(i % 10);
        i /= 10;
        *p++ = zero[lsd];
      } while (i != 0);
      if (value < 0)
      {
        *p++ = '-';
      }
      std::reverse(buf, p);
      return p - buf;
    };
    short int ConvertHex(char buf[], uint64_t value)
    {
      uint64_t i = value;
      char *p = buf;
      do
      {
        int lsd = static_cast<int>(i % 16);
        i /= 16;
        *p++ = digitsHex[lsd];
      } while (i != 0);
      std::reverse(buf, p);
      return p - buf;
    };
  } // namespace detail
  Logstream::Logstream(unsigned int tid)
      : base_len_(5 + 10 + 7 + 6 + 6),
        buffer_len_(0),
        is_out_buffer_(false)
  {
    timeval val;
    gettimeofday(&val, NULL);
    last_time_ = val.tv_sec;
    struct tm tm_time_;
    localtime_r(&val.tv_sec, &tm_time_);
    min_ = tm_time_.tm_min;
    sec_ = tm_time_.tm_sec;
    strftime((reinterpret_cast<char *>(this)), 15, "%m_%d-%H:%M:%S", &tm_time_);

    detail::Convert(utime_ - 1, val.tv_usec + 1000000);
    time_[14] = '.';
    memset(tid_, ' ', 7);
    detail::Convert(tid_ + 1, tid);
    tid_[6] = ' ';
  };
  Logstream::~Logstream(){};
  Logstream &Logstream::Init(const char *file, int len, Log_level level)
  {
    memcpy(log_level_, LogLevelName[level], 6);
    file_ = const_cast<char *>(file);
    len_ = len;
    timeval tm;
    gettimeofday(&tm, NULL);
    detail::Convert(&time_[14], tm.tv_usec + 1000000);
    __time_t diff = tm.tv_sec - last_time_;
    if (diff)
    {
      last_time_ = tm.tv_sec;
      sec_ += diff;
      if (sec_ >= 60) [[unlikely]]
      {
        int add = sec_ / 60;
        min_ += add;
        sec_ = sec_ % 60;
        struct tm tm_time_;
        localtime_r(&tm.tv_sec, &tm_time_);
        strftime(time_, 16, "%m_%d-%H:%M:%S", &tm_time_);
      }
      else
      {
        detail::Convert(&time_[11], sec_ + 100);
        time_[11] = ':';
      }
    }
    time_[14] = '.';
    tid_[0] = ' ';
    return *this;
  };
  void Logstream::Append(const char *address, short int len)
  {
    Is_out_buffer(len);
    memcpy(data_buffer + buffer_len_, address, len);
    buffer_len_ += len;
  };
  void Logstream::Is_out_buffer(short int len)
  {
    if (buffer_len_ + len > _C::SET_LOG_ONE_LOG_MAX_SIZE) [[unlikely]]
    { //需要刷入
      if (is_out_buffer_)
      { //已经越界一次
        Log::output_((reinterpret_cast<char *>(this)) + base_len_, buffer_len_);
        buffer_len_ = 0;
      }
      else
      { //需要打印头
        Log::output_(this, base_len_ + buffer_len_);
        buffer_len_ = 0;
        is_out_buffer_ = true;
      }
    }
  };
  template <typename T>
  void Logstream::Format_integer(T v)
  {
    Is_out_buffer(_C::SET_LOG_FMT_MAX_LEN);
    buffer_len_ += detail::Convert(data_buffer + buffer_len_, v);
  };

  Logstream &Logstream::operator<<(Logendl *end)
  {
    data_buffer[buffer_len_] = ' ';
    data_buffer[buffer_len_ + 1] = ' ';
    buffer_len_ += 2;
    int file_len = strlen(file_);
    memcpy(data_buffer + buffer_len_, file_, file_len);
    buffer_len_ += file_len;
    data_buffer[buffer_len_] = ':';
    buffer_len_ += 1;
    buffer_len_ += detail::Convert(data_buffer + buffer_len_, len_);
    data_buffer[buffer_len_] = 10; //换行
    ++buffer_len_;

    if (is_out_buffer_) [[unlikely]]
    { //不需要打印头
      Log::output_((reinterpret_cast<char *>(this)) + base_len_, buffer_len_);
    }
    else
    {
      Log::output_(this, base_len_ + buffer_len_);
    }
    buffer_len_ = 0;
    return *this;
  };

  Logstream &Logstream::operator<<(bool v)
  {
    Append((v ? "1" : "0"), 1);
    return *this;
  }
  Logstream &Logstream::operator<<(short v)
  {
    *this << static_cast<int>(v);
    return *this;
  };
  Logstream &Logstream::operator<<(unsigned short v)
  {
    *this << static_cast<unsigned int>(v);
    return *this;
  };
  Logstream &Logstream::operator<<(int v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(unsigned int v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(long v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(unsigned long v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(long long v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(unsigned long long v)
  {
    Format_integer(v);
    return *this;
  };
  Logstream &Logstream::operator<<(const void *v)
  {
    uint64_t p = reinterpret_cast<uint64_t>(v);
    Is_out_buffer(_C::SET_LOG_FMT_MAX_LEN);
    char *buf = data_buffer + buffer_len_;
    buf[0] = '0';
    buf[1] = 'x';
    buffer_len_ += 2;
    buffer_len_ += detail::ConvertHex(buf + 2, p);
    return *this;
  };
  Logstream &Logstream::operator<<(float v)
  {
    *this << static_cast<double>(v);
    return *this;
  };
  Logstream &Logstream::operator<<(double v)
  {
    Is_out_buffer(_C::SET_LOG_FMT_MAX_LEN);
    buffer_len_ += snprintf(data_buffer + buffer_len_, _C::SET_LOG_FMT_MAX_LEN, "%.12g", v);
    return *this;
  };
  Logstream &Logstream::operator<<(char v)
  {
    Append(&v, 1);
    return *this;
  }
  Logstream &Logstream::operator<<(const char *str)
  {
    if (str)
    {
      Append(str, strlen(str));
    }
    else
    {
      Append("(null)", 6);
    }
    return *this;
  }
  Logstream &Logstream::operator<<(const unsigned char *str)
  {
    return operator<<(reinterpret_cast<const char *>(str));
  }
  Logstream &Logstream::operator<<(const std::string &v)
  {
    Append(v.c_str(), v.size());
    return *this;
  }
  Logstream &Logstream::operator<<(const timeval &v)
  {
    *this << "sec:" << v.tv_sec << "usec:" << v.tv_usec;
    return *this;
  }

} // namespace net_tools::base